"""
public function--Install&Uninstall
"""
import os
import random

from testcase.utils.Logger import Logger
from yat.test import macro


class LiteInstall:
    def __init__(self, pkg_path, data_path, app_path,
                 func_user_prefix='lite_user_', port_prefix='5',
                 lite_pkg_dirname='lite_pkg'):
        self.log = Logger()
        self.func_user_prefix = func_user_prefix
        self.port_prefix = port_prefix
        self.lite_pkg_dirname = lite_pkg_dirname
        self.pkg_path = pkg_path
        self.data_path = data_path
        self.app_path = app_path
        self.script_path = os.path.join(self.pkg_path, self.lite_pkg_dirname)

    def check_port(self, node, port):
        cmd = ''
        for p in (int(port), int(port) + 1, int(port) + 4,
                  int(port) + 5):
            cmd += f"lsof -i:{p};netstat -anop|grep -w {p};"
        res = node.sh(cmd).result()
        self.log.info(res)
        return res

    def get_user_and_port(self, *nodes):
        self.log.info('开始获取可用用户和端口号')
        tmp_user = self.func_user_prefix + str(
            random.randint(1, 100)).rjust(3, '0')
        tmp_port = self.port_prefix + str(random.randint(100, 900))
        for node in nodes:
            self.log.info(node.ssh_host)
            for j in range(1000):
                cmd = f"cat /etc/passwd|grep -w {tmp_user}"
                res = node.sh(cmd).result()
                self.log.info(res)
                if len(res.strip()) == 0:
                    break
                else:
                    tmp_user = self.func_user_prefix + str(
                        random.randint(1, 1000)).rjust(4, '0')

            for j in range(1000):
                res = self.check_port(node, tmp_port)
                self.log.info(res)
                if len(res.strip()) == 0:
                    break
                else:
                    tmp_port = str(int(tmp_port) + 10)
        return tmp_user, tmp_port

    def prepare_user(self, user, *nodes):
        self.log.info('创建用户和组')
        cmd = f"groupadd {user}"
        cmd += f"\nuseradd -g {user} -d /home/{user} " \
               f"-m -s /bin/bash {user}"
        cmd += f"\necho {macro.COMMON_PASSWD} | passwd --stdin {user}"
        ls_cmd = f'ls -a /home/{user}|grep -E ".bashrc|.bash_profile";'
        cp_cmd = f'\\cp -r /etc/skel/.bashrc /home/{user}/;' \
                 f'\\cp -r /etc/skel/.bash_profile /home/{user}/'
        chmod_cmd = f'if [ -e {self.script_path} ]; ' \
                    f'then chown -R {user}:{user} {self.script_path}; fi'
        for node in nodes:
            self.log.info(f'{node.ssh_host}--{cmd}')
            res = node.sh(cmd).result()
            self.log.info(f'{node.ssh_host}--{res}')

            self.log.info(f'{node.ssh_host}--{ls_cmd}')
            ls_res = node.sh(ls_cmd).result()
            if not ls_res:
                self.log.info('解决偶现groupadd新增用户后，丢失了.bashrc和.bash_profile的问题')
                self.log.info(f'{node.ssh_host}--{cp_cmd}')
                res = node.sh(cp_cmd).result()
                self.log.info(res)
            self.log.info(f'{node.ssh_host}--{chmod_cmd}')
            res = node.sh(chmod_cmd).result()
            self.log.info(res)

    def tar_pkg(self, node):
        self.log.info(f'{node.ssh_host}解压安装包')
        cmd = [f'mkdir -p {self.script_path};',
               f'rm -rf {self.script_path}/*;',
               f'ls {self.pkg_path}|grep -i "\\-lite\\-";',
               f'ls {self.pkg_path}|grep -i "\\-lite\\-"|'
               f'xargs -I {{{{}}}} tar -zxf {self.pkg_path}/'
               f'{{{{}}}} -C {self.script_path};',
               f'ls -l {self.script_path};',
               f'if [ -e {self.pkg_path} ]; '
               f'then chmod 777 -R {self.pkg_path}; fi']
        res = ''
        for c in cmd:
            self.log.info(c)
            res += node.sh(c).result()
        self.log.info(res)
        return res

    def install_single(self, node, port, *params, mode='--mode single'):
        params = f'-D {self.data_path} -R {self.app_path} ' \
                 f'-C "port={port}"' if not params else ' '.join(params)
        cmd = f'cd {self.script_path};' \
              f'echo {macro.COMMON_PASSWD}|' \
              f'sh ./install.sh --start {mode} {params}'
        self.log.info(cmd)
        res = node.sh(cmd).result()
        self.log.info(res)
        return res

    def install_cluster(self, port, *nodes):
        common_cmd = f'cd {self.script_path} && ' \
                     f'echo {macro.COMMON_PASSWD}|sh ./install.sh ' \
                     f'-D {self.data_path} -R {self.app_path} ' \
                     f'-C "port={port}" ' \
                     f'-C "modify_initial_password=false" ' \
                     f'--start'
        res = ''
        i = 0
        while i < len(nodes):
            remotehost = [j for j in nodes if nodes.index(j) != i]
            if i == 0:
                tmp = '--mode primary '
            else:
                tmp = '--mode standby '
            for k, remote_node in enumerate(remotehost):
                tmp += f"-C \"replconninfo{k + 1} = '" \
                       f"localhost={nodes[i].ssh_host} " \
                       f"localport={int(port) + 1} " \
                       f"remotehost={remote_node.ssh_host} " \
                       f"remoteport={int(port) + 1}'\" "
            self.log.info(f'节点{i + 1}--{nodes[i].ssh_host}开始安装')
            self.log.info(f'{common_cmd} {tmp}')
            tmp_res = nodes[i].sh(f'{common_cmd} {tmp}').result()
            self.log.info(tmp_res)
            self.log.info(f'节点{i + 1}--{nodes[i].ssh_host}安装完成')
            res += tmp_res + '\n\n\n'
            i += 1
        return res


class OmInstall:
    def __init__(self, install_dir_path, func_user_prefix='om_user_',
                 port_prefix='5', core_path='/core/corefile'):
        self.log = Logger()
        self.install_dir_path = install_dir_path
        self.func_user_prefix = func_user_prefix
        self.port_prefix = port_prefix
        self.pkg_path = os.path.join(install_dir_path, 'om_pkg')
        self.data_path = os.path.join(install_dir_path, 'om_data')
        self.pkg_script_path = os.path.join(self.pkg_path, 'script')
        self.core_path = core_path
        self.pwd = macro.COMMON_PASSWD

    def check_port(self, node, port):
        cmd = ''
        for p in (int(port), int(port) + 1, int(port) + 4,
                  int(port) + 5):
            cmd += f"lsof -i:{p};netstat -anop|grep -w {p};"
        self.log.info(cmd)
        res = node.sh(cmd).result()
        self.log.info(res)
        return res

    def get_user_and_port(self, *root_nodes):
        self.log.info('开始获取可用用户和端口号')
        tmp_user = self.func_user_prefix + str(
            random.randint(1, 100)).rjust(3, '0')
        tmp_port = self.port_prefix + str(random.randint(100, 900))
        for node in root_nodes:
            self.log.info(node.ssh_host)
            for j in range(1000):
                cmd = f"cat /etc/passwd|grep -w {tmp_user}"
                self.log.info(cmd)
                res = node.sh(cmd).result()
                self.log.info(res)
                if len(res.strip()) == 0:
                    break
                else:
                    tmp_user = self.func_user_prefix + str(
                        random.randint(1, 1000)).rjust(4, '0')

            for j in range(1000):
                self.log.info(node.ssh_host)
                res = self.check_port(node, tmp_port)
                self.log.info(res)
                if len(res.strip()) == 0:
                    break
                else:
                    tmp_port = str(int(tmp_port) + 10)
        return tmp_user, tmp_port

    def prepare_user(self, user, *root_nodes):
        self.log.info('创建用户和组')
        cmd = f"groupadd {user}"
        cmd += f"\nuseradd -g {user} -d /home/{user} " \
               f"-m -s /bin/bash {user}"
        cmd += f"\necho {self.pwd} | passwd --stdin {user}"
        ls_cmd = f'ls -a /home/{user}|grep -E ".bashrc|.bash_profile";'
        cp_cmd = f'\\cp -r /etc/skel/.bashrc /home/{user}/;' \
                 f'\\cp -r /etc/skel/.bash_profile /home/{user}/'
        chmod_cmd = f'if [ -e {self.pkg_path} ]; then echo "exits"; ' \
                    f'else mkdir -p {self.pkg_path}; fi;' \
                    f'chown -R {user}:{user} {self.pkg_path};' \
                    f'chmod 755 {os.path.dirname(self.pkg_path)};' \
                    f'chmod 755 -R {self.pkg_path}'
        for node in root_nodes:
            self.log.info(f'{node.ssh_host}--{cmd}')
            res = node.sh(cmd).result()
            self.log.info(f'{node.ssh_host}--{res}')

            self.log.info(f'{node.ssh_host}--{ls_cmd}')
            ls_res = node.sh(ls_cmd).result()
            if not ls_res:
                self.log.info('解决偶现groupadd新增用户后，丢失了.bashrc和.bash_profile的问题')
                self.log.info(f'{node.ssh_host}--{cp_cmd}')
                res = node.sh(cp_cmd).result()
                self.log.info(res)
            self.log.info(f'{node.ssh_host}--{chmod_cmd}')
            res = node.sh(chmod_cmd).result()
            self.log.info(res)

    def prepare_xml(self, user, port, *root_nodes):
        self.log.info('开始准备xml')
        hostnames = []
        ips = []
        standbys = ''
        base_path = os.path.join(self.data_path, user, 'cluster')
        dn_info = f'{os.path.join(base_path, "dn1")}'
        for i, node in enumerate(root_nodes):
            tmp_ip = node.ssh_host
            tmp_hostname = node.sh('hostname').result().strip()
            ips.append(tmp_ip)
            hostnames.append(tmp_hostname)
            if i != 0:
                standbys += f'''
        <DEVICE sn="{tmp_hostname}">
            <PARAM name="name" value="{tmp_hostname}"/>
            <PARAM name="azName" value="AZ1"/>
            <PARAM name="azPriority" value="1"/>
            <PARAM name="backIp1" value="{tmp_ip}"/>
            <PARAM name="sshIp1" value="{tmp_ip}"/>
        </DEVICE>\n'''
                dn_info += f',{tmp_hostname},{os.path.join(base_path, "dn1")}'
        f_hostnames = ','.join(hostnames)
        f_ips = ','.join(ips)
        xml = f'<?xml version="1.0" encoding="UTF-8"?>\n' \
              f'<ROOT>\n' \
              f'    <CLUSTER>\n' \
              f'        <PARAM name="clusterName" value="{user}"/>\n' \
              f'        <PARAM name="nodeNames" value="{f_hostnames}"/>\n' \
              f'        <PARAM name="backIp1s" value="{f_ips}"/>\n' \
              f'        <PARAM name="gaussdbAppPath" ' \
              f'value="{os.path.join(base_path, "app")}"/>\n' \
              f'        <PARAM name="gaussdbLogPath" ' \
              f'value="{os.path.join(base_path, "gaussdb_log")}"/>\n' \
              f'        <PARAM name="tmpMppdbPath" ' \
              f'value="{os.path.join(base_path, "tmp")}"/>\n' \
              f'        <PARAM name="gaussdbToolPath" ' \
              f'value="{os.path.join(base_path, "tool")}"/>\n' \
              f'        <PARAM name="corePath" value="{self.core_path}"/>\n' \
              f'        <PARAM name="clusterType" value="single-inst"/>\n' \
              f'    </CLUSTER>\n' \
              f'    <DEVICELIST>\n' \
              f'        <DEVICE sn="{hostnames[0]}">\n' \
              f'        <PARAM name="name" value="{hostnames[0]}"/>\n' \
              f'        <PARAM name="azName" value="AZ1"/>\n' \
              f'        <PARAM name="azPriority" value="1"/>\n' \
              f'        <PARAM name="backIp1" value="{ips[0]}"/>\n' \
              f'        <PARAM name="sshIp1" value="{ips[0]}"/>\n' \
              f'        <PARAM name="dataNum" value="1"/>\n' \
              f'        <PARAM name="dataPortBase" value="{port}"/>\n' \
              f'        <PARAM name="dataPortStandby" value="{port}"/>\n' \
              f'        <PARAM name="dataNode1" value="{dn_info}"/>\n' \
              f'        </DEVICE>{standbys}\n' \
              f'    </DEVICELIST>\n' \
              f'</ROOT>'
        xml_path = os.path.join(self.pkg_path, f'{user}.xml')
        res = root_nodes[0].sh(f"echo '{xml}' > {xml_path}").result()
        self.log.info(f'res:{res}')

    def preinstall(self, user, node, params=None):
        self.log.info('准备安装包')
        cmd = f'\\cp -r {os.path.dirname(macro.DB_SCRIPT_PATH)}/*.tar.gz ' \
              f'{self.pkg_path} && cd {self.pkg_path};' \
              f'ls|grep "tar.gz"|xargs -I {{{{}}}} tar -mxf {{{{}}}};' \
              f'ls'
        self.log.info(cmd)
        res = node.sh(cmd).result()
        self.log.info(res)

        self.log.info('开始预安装')
        install_path = os.path.join(self.data_path, user)
        xml_path = os.path.join(self.pkg_path, f'{user}.xml')
        env_path = os.path.join('/home', user, 'gaussdb.bashrc')
        params = f'-U {user} -G {user} -X {xml_path} ' \
                 f'--sep-env-file={env_path}' if not params else params
        cmd = f'''mkdir -p {install_path};
            chown -R {user}:{user} {install_path};
            chmod -R 755 {install_path};
            chmod -R 755 {os.path.dirname(os.path.dirname(self.pkg_path))};
            rm -rf ~/.ssh/*;
            source ~/.bashrc;'''
        self.log.info(cmd)
        res = node.sh(cmd).result()
        self.log.info(res)

        cmd = f'''cd {self.pkg_script_path}
            expect <<EOF
            set timeout 1200
            spawn ./gs_preinstall {params}
            expect {{{{
                "*(yes/no)?" {{{{ send "yes\\n"; exp_continue }}}}
                "*assword:" {{{{ send "{node.ssh_password}\\n" }}}}
            }}}}
            expect {{{{
                "*(yes/no)?" {{{{ send "yes\\n"; exp_continue }}}}
                "*assword:" {{{{ send "{self.pwd}\\n"; exp_continue }}}}
                "*assword:" {{{{ send "{self.pwd}\\n" }}}}
                eof
            }}}}\nEOF'''
        self.log.info(cmd)
        preintall_res = node.sh(cmd).result()
        self.log.info(preintall_res)
        return preintall_res

    def install(self, user, user_node, params=None):
        self.log.info('开始安装')
        xml_path = os.path.join(self.pkg_path, f'{user}.xml')
        env_path = os.path.join('/home', user, 'gaussdb.bashrc')
        params = f'-X {xml_path} --dn-guc=modify_initial_password=' \
                 f'false' if not params else params
        install_cmd = f'gs_install {params}'

        cmd = f'''source {env_path};
            expect <<EOF
            set timeout 1200
            spawn {install_cmd}
            expect {{{{
                "yes/no" {{{{ send "yes\\n"; exp_continue }}}}
                "*database:" {{{{ send "{self.pwd}\\n"; exp_continue }}}}
                "*database:" {{{{ send "{self.pwd}\\n" }}}}
                eof
            }}}}''' + '\nEOF\n'
        self.log.info(f'----{user_node.name}----')
        self.log.info(cmd)
        install_res = user_node.sh(cmd).result()
        self.log.info(install_res)

        return install_res


class CmInstall:
    def __init__(self, install_dir_path, user):
        self.user = user
        self.log = Logger()
        self.om = OmInstall(install_dir_path)
        self.env_path = os.path.join('/home', user, 'gaussdb.bashrc')
        self.pkg_path = os.path.join(install_dir_path)
        self.data_path = os.path.join(install_dir_path, 'cm_data')
        self.xml_path = os.path.join(install_dir_path, f'{user}.xml')
        self.cm_path = os.path.join(self.data_path, self.user, 'cluster', 'cm')

    def update_xml_for_cm(self, *root_nodes, exec_node=0):
        self.log.info('修改xml为CM方式')
        hostnames = []
        ips = []
        for node in root_nodes:
            ips.append(node.ssh_host)
            hostnames.append(node.sh('hostname').result().strip())

        f_hostnames = ','.join(hostnames)
        f_ips = ','.join(ips)
        _, cm_port = self.om.get_user_and_port(*root_nodes)
        xml_path = os.path.join(self.pkg_path, f'{self.user}.xml')
        sed_cmd = f'sed -i "/dataNum/i\\        ' \
                  f'<PARAM name=\\"cmsNum\\" value=\\"1\\"/>" ' \
                  f'{xml_path}' \
                  f'\nsed -i "/dataNum/i\\        ' \
                  f'<PARAM name=\\"cmServerPortBase\\" ' \
                  f'value=\\"{cm_port}\\"/>" {xml_path}' \
                  f'\nsed -i "/dataNum/i\\        ' \
                  f'<PARAM name=\\"cmServerPortStandby\\" ' \
                  f'value=\\"{cm_port}\\"/>" {xml_path}' \
                  f'\nsed -i "/dataNum/i\\        ' \
                  f'<PARAM name=\\"cmServerlevel\\" value=\\"1\\"/>" ' \
                  f'{xml_path}' \
                  f'\nsed -i "/dataNum/i\\        ' \
                  f'<PARAM name=\\"cmServerListenIp1\\" ' \
                  f'value=\\"{f_ips}\\"/>" {xml_path}' \
                  f'\nsed -i "/dataNum/i\\        ' \
                  f'<PARAM name=\\"cmServerRelation\\" ' \
                  f'value=\\"{f_hostnames}\\"/>" {xml_path}' \
                  f'\nsed -i "/azPriority/a\\        ' \
                  f'<PARAM name=\\"cmDir\\" value=\\"{self.cm_path}\\"/>"' \
                  f' {xml_path}' \
                  f'\nsed -i "/dataPortStandby/d" {xml_path}'
        self.log.info(f'----{root_nodes[exec_node].name}----')
        self.log.info(sed_cmd)
        root_nodes[exec_node].sh(sed_cmd).result()

        cm_res = root_nodes[exec_node].sh(f'grep "cm" {xml_path}').result()
        self.log.info(cm_res)
        return cm_res

    def cm_install(self, user_node, params=None):
        ls_cmd = f'cd {self.pkg_path};ls|grep "CM"|grep "tar.gz"'
        self.log.info(ls_cmd)
        ls_res = user_node.sh(ls_cmd).result().strip()
        self.log.info(ls_res)
        if not ls_res:
            tar_cmd = f'cd {self.pkg_path};' \
                      f'ls|grep -i "\\-all\\-"|' \
                      f'xargs -I {{{{}}}} tar -xf {{{{}}}}'
            self.log.info(tar_cmd)
            tar_res = user_node.sh(tar_cmd).result().strip()
            self.log.info(tar_res)

            cm_pkg_name = user_node.sh(ls_cmd).result().strip()
        else:
            cm_pkg_name = ls_res
        self.log.info(cm_pkg_name)
        cm_pkg_path = os.path.join(self.pkg_path, cm_pkg_name)

        cm_params = params if params else f'-X {self.xml_path} ' \
                                          f'-e {self.env_path} ' \
                                          f'--cmpkg={cm_pkg_path}'
        cmd = f'''source {self.env_path};
        cd $GPHOME;
        ls|grep -i "cm"|xargs -I {{{{}}}} tar -mxf {{{{}}}};
        cd tool/cm_tool;
        expect <<EOF
        set timeout 300
        spawn ./cm_install {cm_params}
        expect {{{{
            "*ca cert*" {{{{ send "{macro.COMMON_PASSWD}\\n"; exp_continue }}}}
            "*ca cert*" {{{{ send "{macro.COMMON_PASSWD}\\n" }}}}
            eof
        }}}}''' + '\nEOF'
        self.log.info(f'----{user_node.name}----')
        self.log.info(cmd)
        res = user_node.sh(cmd).result()
        self.log.info(res)
        return res


class LiteUnInstall:
    def __init__(self, pkg_path):
        self.log = Logger()
        self.lite_pkg_dirname = 'lite_pkg'
        self.pkg_path = pkg_path
        self.script_path = os.path.join(self.pkg_path, self.lite_pkg_dirname)

    def uninstall(self, *nodes, mode='--delete-data'):
        cmd = f'cd {self.script_path};' \
              f'sh uninstall.sh {mode}'
        res = ''
        for node in nodes:
            self.log.info(f'----{node.name}----')
            self.log.info(f'{cmd}')
            res += node.sh(cmd).result() + '\n\n'
        self.log.info(res)
        return res

    def clear_env(self, *nodes):
        res = ''
        for node in nodes:
            cmd = f"ls /home|grep \"lite_\"|" \
                  f"xargs -I {{{{}}}} ps -u {{{{}}}}|grep -v PID|" \
                  f"awk '{{{{print $1}}}}'|" \
                  f"xargs -I {{{{}}}} kill -9 {{{{}}}};" \
                  f"rm -rf {self.script_path};" \
                  f"ls /home|grep \"lite_\"|" \
                  f"xargs -I {{{{}}}} userdel -r {{{{}}}}"
            self.log.info(f'----{node.name}----')
            self.log.info(f'{cmd}')
            res += node.sh(cmd).result() + '\n\n'
        self.log.info(res)
        return res


class OmUnInstall:
    def __init__(self, user, install_dir_path):
        self.log = Logger()
        self.user = user
        self.install_dir_path = os.path.join(install_dir_path, 'om_pkg')
        self.data_dir_path = os.path.join(install_dir_path, 'om_data')
        self.pkg_script_path = os.path.join(self.install_dir_path, 'script')
        self.xml_path = os.path.join(self.install_dir_path, f'{user}.xml')
        self.env_path = os.path.join('/home', user, 'gaussdb.bashrc')

    def uninstall(self, user_node, params='--delete-data'):
        cmd = f'source {self.env_path};' \
              f'gs_uninstall {params}'
        self.log.info(f'----{user_node.name}----')
        self.log.info(cmd)
        res = user_node.sh(cmd).result()
        self.log.info(res)

    def clear_env(self, *root_nodes):
        res = ''
        for node in root_nodes:
            cmd = f"ls /home|grep \"{self.user}\"|" \
                  f"xargs -I {{{{}}}} ps -u {{{{}}}}|grep -v PID|" \
                  f"awk '{{{{print $1}}}}'|" \
                  f"xargs -I {{{{}}}} kill -9 {{{{}}}};" \
                  f"rm -rf {self.install_dir_path};" \
                  f"rm -rf {self.data_dir_path};" \
                  f"ls /home|grep \"{self.user}\"|" \
                  f"xargs -I {{{{}}}} userdel -r {{{{}}}}"
            self.log.info(f'----{node.name}----')
            self.log.info(f'{cmd}')
            res += node.sh(cmd).result() + '\n\n'
        self.log.info(res)
        return res

    def create_trust(self, *root_nodes):
        hosts = "\n".join([node.ssh_host for node in root_nodes])
        cmd = f'''cd {self.pkg_script_path};
        echo "{hosts}" > tmp_hosts;
        expect <<EOF
        set timeout 300
        spawn ./gs_sshexkey -f tmp_hosts
        expect {{{{
            "*assword:*" {{{{ send "{macro.COMMON_PASSWD}\n"; exp_continue }}}}
            eof
        }}}}''' + '\nEOF\n' + 'rm -rf tmp_hosts;'
        self.log.info(cmd)
        res = root_nodes[-1].sh(cmd).result()
        self.log.info(res)
        assert 'Successfully created SSH trust' in res

    def gs_postuninstall(self, root_node, param='--delete-user --delete-group'):
        cmd = f'source {self.env_path};' \
              f'gs_postuninstall -U {self.user} -X {self.xml_path} {param}'
        self.log.info(f'----{root_node.name}----')
        self.log.info(cmd)
        res = root_node.sh(cmd).result()
        self.log.info(res)
        return res


class CmUnInstall:
    def __init__(self, install_dir_path, user):
        self.log = Logger()
        self.user = user
        self.xml_path = os.path.join(install_dir_path, f'{user}.xml')
        self.env_path = os.path.join('/home', user, 'gaussdb.bashrc')

    def cm_uninstall(self, user_node, params='--deleteData'):
        cmd = f'source {self.env_path};' \
              f'cd $GPHOME;' \
              f'ls|grep -i "cm"|xargs -I {{{{}}}} tar -mxf {{{{}}}};' \
              f'cd tool/cm_tool;' \
              f'sed -i \'/cm/d\' {self.xml_path};' \
              f'./cm_uninstall -X {self.xml_path} {params}'
        self.log.info(f'----{user_node.name}----')
        self.log.info(cmd)
        res = user_node.sh(cmd).result()
        self.log.info(res)
        return res
